package xyz.hubery.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.Cached;
import com.alicp.jetcache.anno.CreateCache;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import xyz.hubery.domain.*;
import xyz.hubery.dto.AdminBankDTO;
import xyz.hubery.dto.UserDTO;
import xyz.hubery.feign.AdminBankServiceFeign;
import xyz.hubery.feign.AdminServiceFeign;
import xyz.hubery.feign.UserFeign;
import xyz.hubery.mapper.CashRechargeMapper;
import xyz.hubery.model.BuyGCNParam;
import xyz.hubery.model.CashTradeVo;
import xyz.hubery.service.*;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CashRechargeServiceImpl extends ServiceImpl<CashRechargeMapper, CashRecharge> implements CashRechargeService {

    @Autowired
    private UserFeign userFeign;
    @Autowired
    private ConfigService configService;
    @Autowired
    private AdminBankServiceFeign adminBankServiceFeign;
    @Autowired
    private CoinService coinService;
    @CreateCache(name = "CASH_RECHARGE_LOCK:",expire = 100,timeUnit = TimeUnit.SECONDS)
    private Cache<String,String> cache;
    @Autowired
    private AdminServiceFeign adminServiceFeign;
    @Autowired
    private CashRechargeAuditRecordService cashRechargeAuditRecordService;
    @Autowired
    private AccountService accountService;

    @Override
    public Page<CashRecharge> findPage(Long userId, String userName, String mobile, Byte status, Integer numMin, Integer numMax, String startTime, String endTime, Page<CashRecharge> page) {
        page.addOrder(OrderItem.desc("last_update_time"));
        LambdaQueryWrapper<CashRecharge> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(userId != null, CashRecharge::getUserId, userId);
        wrapper.eq(status != null, CashRecharge::getStatus, status);
        wrapper.between(numMin != null && numMax != null, CashRecharge::getNum, numMin, numMax);
        wrapper.between(startTime != null && endTime != null, CashRecharge::getCreated, startTime, endTime);

        Page<CashRecharge> cashRechargePage = page(page, wrapper);
        List<Long> userIds = new ArrayList<>();
        // 获取用户id列表
        if (userId != null) {  // 直接根据userId查询
            userIds.add(userId);
        } else { // 提取用户id列表
            userIds = cashRechargePage.getRecords().stream().map(CashRecharge::getUserId).distinct().collect(Collectors.toList());
        }
        if (CollectionUtils.isEmpty(userIds)) {
            return cashRechargePage;
        }
        // 远程调用根据用户id列表查询用户信息
        List<UserDTO> userDTOList = userFeign.getBasicUserInfo(userIds);
        // 转换为map结构
        Map<Long, UserDTO> userDTOMap = userDTOList.stream().collect(Collectors.toMap(UserDTO::getId, user -> user));
        // 提取出符合条件的充值记录
        List<CashRecharge> filteredRecords = new ArrayList<>();
        Set<Long> filterIds = new HashSet<Long>();
        for (UserDTO userDTO : userDTOList) {
            boolean match = true;
            if (!StringUtils.isEmpty(userName) && !userDTO.getUsername().contains(userName)) { //根据用户名进行匹配
                match = false;
            }
            if (!StringUtils.isEmpty(mobile) && !userDTO.getMobile().contains(mobile)) { //根据手机号进行匹配
                match = false;
            }
            if (match) {
                filterIds.add(userDTO.getId());
            }
        }

        List<CashRecharge> records = cashRechargePage.getRecords();
        for (CashRecharge record : records) {
            if (filterIds.contains(record.getUserId())) {
                // 填充用户信息
                UserDTO userDTO = userDTOMap.get(record.getUserId());
                record.setUsername(userDTO.getUsername());
                record.setMobile(userDTO.getMobile());
                record.setRealName(userDTO.getRealName());
                filteredRecords.add(record);
            }
        }

        // 替换为过滤后的记录
        cashRechargePage.setRecords(filteredRecords);
        cashRechargePage.setTotal(filteredRecords.size());
        return cashRechargePage;

    }


    @Override
    public CashTradeVo buy(Long userId, BuyGCNParam param) {
        // 1、校验现金购买参数
        checkBuyGCNParam(param);
        // 2、查询银行卡信息
        // 远程调用admin-service服务
        List<AdminBankDTO> bankDTOList = adminBankServiceFeign.listAdminBanks();
        // 负载均衡选择一个银行卡
        AdminBankDTO bank = loadbalancer(bankDTOList);
        // 3、在数据库里面插入一条充值的记录
        CashRecharge cashRecharge = new CashRecharge();
        cashRecharge.setUserId(userId);//user_id
        cashRecharge.setCoinId(param.getCoinId());//coin_id
        // 根据coinid查询coin
        Coin coin = coinService.getById(param.getCoinId());
        if (coin == null) {
            throw new IllegalArgumentException("币种不存在");
        }
        cashRecharge.setCoinName(coin.getName());//coin_name
        cashRecharge.setNum(param.getNum());//num
        // 查询手续费
        Config feeconfig = configService.getConfigByCode("WITHDRAW_POUNDAGE_RATE");
        cashRecharge.setFee(new BigDecimal(feeconfig.getValue()));//fee
        //feecoin
        // 计算成交额：注意这里不能使用前端传递过来的mum，因为前端传递过来的mum可能会被篡改或者浏览器缓存
        Config buyconfig = configService.getConfigByCode("CNY2USDT");
        BigDecimal realMum = param.getNum().multiply(new BigDecimal(buyconfig.getValue()));
        cashRecharge.setMum(realMum);//mum
        cashRecharge.setType("linepay");//type
        // 使用雪花算法生成订单号
        cashRecharge.setTradeno(IdWorker.getIdStr());//tradeno
        cashRecharge.setOuttradeno(null);//outtradeno
        String remark = RandomUtil.randomNumbers(6);
        cashRecharge.setRemark(remark);//remark
        cashRecharge.setAuditRemark(null);//audit_remark
        cashRecharge.setStep((byte) 1);//step
        cashRecharge.setStatus((byte) 0);//status
        cashRecharge.setName(bank.getName());//name
        cashRecharge.setBankCard(bank.getBankName());//bank_name
        cashRecharge.setBankCard(bank.getBankCard());//bank_card
        // 插入
        boolean save = save(cashRecharge);
        if (!save) {
            throw new RuntimeException("充值记录插入失败");
        }
        // 4、返回充值成功信息
        CashTradeVo tradeVo = new CashTradeVo();
        tradeVo.setName(bank.getName());
        tradeVo.setBankName(bank.getBankName());
        tradeVo.setBankCard(bank.getBankCard());
        tradeVo.setAmount(realMum);
        tradeVo.setRemark(remark);
        tradeVo.setStatus((byte) 0);
        return tradeVo;
    }

    /**
     * 随机获得一个银行卡
     *
     * @param bankDTOList
     * @return
     */
    private AdminBankDTO loadbalancer(List<AdminBankDTO> bankDTOList) {
        if (CollectionUtils.isEmpty(bankDTOList)) {
            throw new RuntimeException("银行卡为空");
        }
        Random random = new Random();
        return bankDTOList.get(random.nextInt(bankDTOList.size()));
    }

    /**
     * 校验购买GCN参数
     *
     * @param param
     */
    private void checkBuyGCNParam(BuyGCNParam param) {
        // 数量不能小于系统设置的值
        Config config = configService.getConfigByCode("MIN_BUY_NUM");
        BigDecimal minNum = new BigDecimal(config.getValue());
        if (param.getNum().compareTo(minNum) < 0) {
            throw new RuntimeException("购买数量不能小于" + minNum);
        }
    }

    @Override
    public Page<CashRecharge> findCashRechargePage(Page<CashRecharge> page, Long userId, Byte status) {
        return page(page,
                new LambdaQueryWrapper<CashRecharge>()
                        .eq(CashRecharge::getUserId, userId)
                        .eq(status != null, CashRecharge::getStatus, status)
        );
    }


    @Transactional
    @Override
    public boolean updateStatus(Long adminId,CashRechargeAuditRecord cashRechargeAuditRecord,String businessType) {
        // 有可能有多个员工同时审核，所以要使用分布式锁进行隔离
        boolean b = cache.tryLockAndRun(cashRechargeAuditRecord.getId()+"",100,TimeUnit.SECONDS,()->{
            Long id = cashRechargeAuditRecord.getId();
            CashRecharge cashRecharge = getById(id); //当前充值记录
            if(cashRecharge == null) {
                throw new IllegalArgumentException("充值记录不存在");
            }
            if(cashRecharge.equals(1)) { // 已经审核通过了不需要再次审核
                throw new IllegalArgumentException("已经审核通过了不需要再次审核");
            }
            // 1、添加一条审核记录
            CashRechargeAuditRecord auditRecord = new CashRechargeAuditRecord();
            auditRecord.setOrderId(cashRecharge.getId());
            auditRecord.setStatus(cashRechargeAuditRecord.getStatus());
            auditRecord.setRemark(cashRechargeAuditRecord.getRemark());
            auditRecord.setStep((byte)(cashRecharge.getStep()+1));
            auditRecord.setAuditUserId(adminId);
            auditRecord.setAuditUserName(adminServiceFeign.getAdminName(adminId));
            boolean save = cashRechargeAuditRecordService.save(auditRecord);
            if(save){
                cashRecharge.setAuditRemark(cashRechargeAuditRecord.getRemark());
                cashRecharge.setStep((byte)(cashRecharge.getStep()+1));
                cashRecharge.setStatus(cashRechargeAuditRecord.getStatus());
                cashRecharge.setLastTime(new Date()); // 完成时间
                // 2、审核通过
                if(cashRechargeAuditRecord.getStatus().equals((byte)1)){
                    // 修改账户余额
                    accountService.transferAmount(adminId,cashRecharge.getUserId(), cashRecharge.getCoinName(), cashRecharge.getId(),cashRecharge.getMum(),cashRecharge.getFee(),"recharge_into","充值" );
                }
                updateById(cashRecharge);
            }
        });
        return b;
    }
}
